// bslh_defaultseededhashalgorithm.t.cpp                              -*-C++-*-
#include <bslh_defaultseededhashalgorithm.h>

#include <bslh_seedgenerator.h>

#include <bslmf_issame.h>

#include <bsls_assert.h>
#include <bsls_asserttest.h>
#include <bsls_bsltestutil.h>
#include <bsls_platform.h>
#include <bsls_bsltestutil.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

using namespace BloombergLP;
using namespace bslh;


//=============================================================================
//                                  TEST PLAN
//-----------------------------------------------------------------------------
//                                  Overview
//                                  --------
// The component under test is a `bslh` hashing algorithm.  The basic test plan
// is to compare the output of the function call operator with the expected
// output generated by a known-good implementation of the hashing algorithm.
// The component will also be tested for conformance to the requirements on
// `bslh` hashing algorithms, outlined in the `bslh` package level
// documentation.
//-----------------------------------------------------------------------------
// TYPES
// [ 4] typedef InternalHashAlgorithm::result_type result_type;
//
// CONSTANTS
// [ 5] enum { k_SEED_LENGTH = InternalHashAlgorithm::k_SEED_LENGTH };
//
// CREATORS
// [ 2] DefaultSeededHashAlgorithm(const void *seed);
// [ 2] ~DefaultSeededHashAlgorithm();
//
// MANIPULATORS
// [ 3] void operator()(void const* key, size_t len);
// [ 3] result_type computeHash();
// ----------------------------------------------------------------------------
// [ 1] BREATHING TEST
// [ 6] HASHING AS SEPARATE SEGMENTS
// [ 7] USAGE EXAMPLE
//-----------------------------------------------------------------------------

// ============================================================================
//                     STANDARD BSL ASSERT TEST FUNCTION
// ----------------------------------------------------------------------------

namespace {

int testStatus = 0;

void aSsErT(bool condition, const char *message, int line)
{
    if (condition) {
        printf("Error " __FILE__ "(%d): %s    (failed)\n", line, message);
        fflush(stdout);

        if (0 <= testStatus && testStatus <= 100) {
            ++testStatus;
        }
    }
}

}  // close unnamed namespace

// ============================================================================
//               STANDARD BSL TEST DRIVER MACRO ABBREVIATIONS
// ----------------------------------------------------------------------------

#define ASSERT       BSLS_BSLTESTUTIL_ASSERT
#define ASSERTV      BSLS_BSLTESTUTIL_ASSERTV

#define LOOP_ASSERT  BSLS_BSLTESTUTIL_LOOP_ASSERT
#define LOOP0_ASSERT BSLS_BSLTESTUTIL_LOOP0_ASSERT
#define LOOP1_ASSERT BSLS_BSLTESTUTIL_LOOP1_ASSERT
#define LOOP2_ASSERT BSLS_BSLTESTUTIL_LOOP2_ASSERT
#define LOOP3_ASSERT BSLS_BSLTESTUTIL_LOOP3_ASSERT
#define LOOP4_ASSERT BSLS_BSLTESTUTIL_LOOP4_ASSERT
#define LOOP5_ASSERT BSLS_BSLTESTUTIL_LOOP5_ASSERT
#define LOOP6_ASSERT BSLS_BSLTESTUTIL_LOOP6_ASSERT

#define Q            BSLS_BSLTESTUTIL_Q   // Quote identifier literally.
#define P            BSLS_BSLTESTUTIL_P   // Print identifier and value.
#define P_           BSLS_BSLTESTUTIL_P_  // P(X) without '\n'.
#define T_           BSLS_BSLTESTUTIL_T_  // Print a tab (w/o newline).
#define L_           BSLS_BSLTESTUTIL_L_  // current Line number

// ============================================================================
//                  NEGATIVE-TEST MACRO ABBREVIATIONS
// ----------------------------------------------------------------------------

#define ASSERT_SAFE_PASS(EXPR) BSLS_ASSERTTEST_ASSERT_SAFE_PASS(EXPR)
#define ASSERT_SAFE_FAIL(EXPR) BSLS_ASSERTTEST_ASSERT_SAFE_FAIL(EXPR)
#define ASSERT_PASS(EXPR)      BSLS_ASSERTTEST_ASSERT_PASS(EXPR)
#define ASSERT_FAIL(EXPR)      BSLS_ASSERTTEST_ASSERT_FAIL(EXPR)
#define ASSERT_OPT_PASS(EXPR)  BSLS_ASSERTTEST_ASSERT_OPT_PASS(EXPR)
#define ASSERT_OPT_FAIL(EXPR)  BSLS_ASSERTTEST_ASSERT_OPT_FAIL(EXPR)

// ============================================================================
//                  PRINTF FORMAT MACRO ABBREVIATIONS
// ----------------------------------------------------------------------------

#define ZU BSLS_BSLTESTUTIL_FORMAT_ZU

//=============================================================================
//                 GLOBAL TYPEDEFS AND DATA FOR TESTING
//-----------------------------------------------------------------------------

typedef bsls::Types::Uint64        Uint64;
typedef DefaultSeededHashAlgorithm Obj;
const char globalSeed[DefaultSeededHashAlgorithm::k_SEED_LENGTH] = { 0 };

// ============================================================================
//                  TYPES, CLASSES, FUNCTIONS, AND CONSTANTS
// ----------------------------------------------------------------------------

namespace {
namespace u {

const Uint64 minus1 = static_cast<Uint64>(0) - 1;

struct RandGen {
    // DATA
    Uint64 d_accum;

    // PRIVATE MANIPULATOR
    void munge()
    {
        d_accum = d_accum * 6364136223846793005ULL + 1442695040888963407ULL;
    }

  public:
    // CREATOR
    RandGen() : d_accum(0) {}

    // MANIPULATORS

    /// MMIX Linear Congruential Generator algorithm by Donald Knuth
    unsigned num(Uint64 seed = minus1)
    {
        if (minus1 != seed) {
            d_accum = seed;

            munge();
            d_accum ^= d_accum >> 32;
            munge();
            d_accum ^= d_accum >> 32;
            munge();
        }

        munge();

        return static_cast<unsigned>(d_accum >> 32);
    }

    void randMemory(void *memory, size_t size);

    template <class TYPE>
    void randVal(TYPE *var);
};

void RandGen::randMemory(void *memory, size_t size)
{
    char *ram = static_cast<char *>(memory);
    char *end = ram + size;

    for (size_t toCopy; ram < end; ram += toCopy) {
        unsigned randVal = num();
        toCopy = std::min<size_t>(sizeof(unsigned), end - ram);

#ifdef BSLS_PLATFORM_IS_BIG_ENDIAN
        // always change to little endian

        randVal = bsls::ByteOrderUtil::swapBytes32(randVal);
#endif

        memcpy(ram, &randVal, toCopy);
    }
}

template <class TYPE>
void RandGen::randVal(TYPE *var)
{
    Uint64 accum = num();
    accum |= (static_cast<Uint64>(num()) << 32);

    *var = static_cast<TYPE>(accum);
}

}  // close namespace u
}  // close namespace

//=============================================================================
//                    COMPONENTS REQUIRED FOR USAGE EXAMPLE
//-----------------------------------------------------------------------------

/// Generates a random number.
class SomeRNG {

  public:
    /// Type of the number that will be returned.
    typedef unsigned long long result_type;

    /// Return a pseudo-random number.
    result_type operator()();
};
SomeRNG::result_type SomeRNG::operator()() {
    return 49872534ULL; // NOTE: not actually random
}

//=============================================================================
//                             USAGE EXAMPLE
//-----------------------------------------------------------------------------
///Usage
///-----
// This section illustrates intended usage of this component.
//
///Example: Creating and Using a Hash Table
/// - - - - - - - - - - - - - - - - - - - -
// Suppose we have any array of types that define `operator==`, and we want a
// fast way to find out if values are contained in the array.  We can create a
// `HashTable` data structure that is capable of looking up values in O(1)
// time.
//
// Further suppose that we will be storing futures (the financial instruments)
// in this table.  Since futures have standardized names, we don't have to
// worry about any malicious values causing collisions.  We will want to use a
// general purpose hashing algorithm with a good hash distribution and good
// speed.  This algorithm will need to be in the form of a hash functor -- an
// object that will take objects stored in our array as input, and yield an
// integer value.  The functor can pass the attributes of `TYPE` that are
// salient to hashing into the hashing algorithm, and then return the hash that
// is produced.
//
// We can use the result of the hash function to index into our array of
// `buckets`.  Each `bucket` is simply a pointer to a value in our original
// array of `TYPE` objects.
//
// First, we define our `HashTable` template class, with the two type
// parameters: `TYPE` (the type being referenced) and `HASHER` (a functor that
// produces the hash).
// ```

    /// This class template implements a hash table providing fast lookup of
    /// an external, non-owned, array of values of (template parameter)
    /// `TYPE`.
    ///
    /// The (template parameter) `TYPE` shall have a transitive, symmetric
    /// `operator==` function.  There is no requirement that it have any
    /// kind of creator defined.
    ///
    /// The `HASHER` template parameter type must be a functor with a method
    /// having the following signature:
    /// ```
    /// size_t operator()(TYPE)  const;
    ///                  -OR-
    /// size_t operator()(const TYPE&) const;
    /// ```
    /// and `HASHER` shall have a publicly accessible default constructor
    /// and destructor.
    ///
    /// Note that this hash table has numerous simplifications because we
    /// know the size of the array and never have to resize the table.
    template <class TYPE, class HASHER>
    class HashTable {

        // DATA
        const TYPE       *d_values;          // Array of values table is to
                                             // hold
        size_t            d_numValues;       // Length of `d_values`.
        const TYPE      **d_bucketArray;     // Contains ptrs into d_values'
        size_t            d_bucketArrayMask; // Will always be `2^N - 1`.
        HASHER            d_hasher;          // User supplied hashing algorithm

      private:
        // PRIVATE ACCESSORS

        /// Look up the specified `value`, having the specified `hashValue`,
        /// and load its index in `d_bucketArray` into the specified `idx`.
        /// If not found, return the vacant entry in `d_bucketArray` where
        /// it should be inserted.  Return `true` if `value` is found and
        /// `false` otherwise.
        bool lookup(size_t      *idx,
                    const TYPE&  value,
                    size_t       hashValue) const;

      public:
        // CREATORS

        /// Create a hash table referring to the specified `valuesArray`
        /// having length of the specified `numValues`.  No value in
        /// `valuesArray` shall have the same value as any of the other
        /// values in `valuesArray`
        HashTable(const TYPE *valuesArray,
                  size_t      numValues);

        /// Free up memory used by this hash table.
        ~HashTable();

        // ACCESSORS

        /// Return true if the specified `value` is found in the table and
        /// false otherwise.
        bool contains(const TYPE& value) const;
    };
// ```
// Then, we define a `Future` class, which holds a c-string `name`, char
// `callMonth`, and short `callYear`.
// ```

    /// This class identifies a future contract.  It tracks the name, call
    /// month and year of the contract it represents, and allows equality
    /// comparison.
    class Future {

        // DATA
        const char *d_name;    // held, not owned
        const char  d_callMonth;
        const short d_callYear;

      public:
        // CREATORS

        /// Create a `Future` object out of the specified `name`,
        /// `callMonth`, and `callYear`.
        Future(const char *name, const char callMonth, const short callYear)
        : d_name(name), d_callMonth(callMonth), d_callYear(callYear)
        {}

        /// Create a `Future` with default values.
        Future() : d_name(""), d_callMonth('\0'), d_callYear(0)
        {}

        // ACCESSORS

        /// Return the month that this future expires.
        const char * getMonth() const
        {
            return &d_callMonth;
        }

        /// Return the name of this future
        const char * getName() const
        {
            return d_name;
        }

        /// Return the year that this future expires
        const short * getYear() const
        {
            return &d_callYear;
        }

        /// Compare this to the specified `other` object and return true if
        /// they are equal
        bool operator==(const Future& other) const
        {
            return (!strcmp(d_name, other.d_name))  &&
               d_callMonth == other.d_callMonth &&
               d_callYear  == other.d_callYear;
        }
    };

    /// Compare compare the specified `lhs` and `rhs` objects and return
    /// true if they are not equal
    bool operator!=(const Future& lhs, const Future& rhs)
    {
        return !(lhs == rhs);
    }

// ```
// Next, we need a hash functor for `Future`.  We are going to use the
// `DefaultSeededHashAlgorithm` because it is a fast, general purpose hashing
// algorithm that will provide an easy way to combine the attributes of
// `Future` objects that are salient to hashing into one reasonable hash that
// will distribute the items evenly throughout the hash table.  Moreover, when
// a new hashing algorithm is discovered to be a better default, we can be
// automatically be upgraded to use it as soon as
// `bslh::DefaultSeededHashAlgorithm` is updated.
// ```

    /// This struct is a functor that will apply the
    /// `DefaultSeededHashAlgorithm` to objects of type `Future`, using a
    /// generated seed.
    struct HashFuture {

        /// Return the hash of the of the specified `future`.  Note that
        /// this uses the `DefaultSeededHashAlgorithm` to quickly combine
        /// the attributes of `Future` objects that are salient to hashing
        /// into a hash suitable for a hash table.
        size_t operator()(const Future& future) const
        {
// ```
// Then, we use a `bslh::SeedGenerator` combined with a RNG (implementation not
// shown), to generate the seeds for our algorithm.
// ```
            char seed[DefaultSeededHashAlgorithm::k_SEED_LENGTH];
            SeedGenerator<SomeRNG> seedGenerator;
            seedGenerator.generateSeed(seed,
                                    DefaultSeededHashAlgorithm::k_SEED_LENGTH);

            DefaultSeededHashAlgorithm hash(seed);
// ```
// Next, after seeding our algorithm, we pass data into it and operate on it
// just as easily as for a non-seeded algorithm
// ```

            hash(future.getName(),  strlen(future.getName()));
            hash(future.getMonth(), sizeof(char));
            hash(future.getYear(),  sizeof(short));

            return static_cast<size_t>(hash.computeHash());
        }
    };

//=============================================================================
//                     ELIDED USAGE EXAMPLE IMPLEMENTATIONS
//-----------------------------------------------------------------------------

// PRIVATE ACCESSORS
template <class TYPE, class HASHER>
bool HashTable<TYPE, HASHER>::lookup(size_t      *idx,
                                     const TYPE&  value,
                                     size_t       hashValue) const
{
    const TYPE *ptr;
    for (*idx = hashValue & d_bucketArrayMask; (ptr = d_bucketArray[*idx]);
                                   *idx = (*idx + 1) & d_bucketArrayMask) {
        if (value == *ptr) {
            return true;                                              // RETURN
        }
    }
    // value was not found in table

    return false;
}

// CREATORS
template <class TYPE, class HASHER>
HashTable<TYPE, HASHER>::HashTable(const TYPE *valuesArray,
                                   size_t      numValues)
: d_values(valuesArray)
, d_numValues(numValues)
, d_hasher()
{
    size_t bucketArrayLength = 4;
    while (bucketArrayLength < numValues * 4) {
        bucketArrayLength *= 2;

    }
    d_bucketArrayMask = bucketArrayLength - 1;
    d_bucketArray = new const TYPE *[bucketArrayLength];
    memset(d_bucketArray,  0, bucketArrayLength * sizeof(TYPE *));

    for (unsigned i = 0; i < numValues; ++i) {
        const TYPE& value = d_values[i];
        size_t idx;
        bool result = lookup(&idx, value, d_hasher(value));
        BSLS_ASSERT_OPT(!result);  (void)result;
        d_bucketArray[idx] = &d_values[i];
    }
}

template <class TYPE, class HASHER>
HashTable<TYPE, HASHER>::~HashTable()
{
    delete [] d_bucketArray;
}

// ACCESSORS
template <class TYPE, class HASHER>
bool HashTable<TYPE, HASHER>::contains(const TYPE& value) const
{
    size_t idx;
    return lookup(&idx, value, d_hasher(value));
}

// ============================================================================
//                            MAIN PROGRAM
// ----------------------------------------------------------------------------

int main(int argc, char *argv[])
{
    int                 test = argc > 1 ? atoi(argv[1]) : 0;
    bool             verbose = argc > 2;
    bool         veryVerbose = argc > 3;
    bool     veryVeryVerbose = argc > 4; (void) veryVeryVerbose;
    bool veryVeryVeryVerbose = argc > 5; (void) veryVeryVeryVerbose;

    (void)veryVeryVeryVerbose;  // suppress warning

    printf("TEST " __FILE__ " CASE %d\n", test);

    switch (test) { case 0:
      case 7: {
        // --------------------------------------------------------------------
        // USAGE EXAMPLE
        //   The hashing algorithm can be used to create more powerful
        //   components such as functors that can be used to power hash tables.
        //
        // Concerns:
        // 1. The usage example provided in the component header file compiles,
        //    links, and runs as shown.
        //
        // Plan:
        // 1. Incorporate usage example from header into test driver (C-1)
        //
        // Testing:
        //   USAGE EXAMPLE
        // --------------------------------------------------------------------

        if (verbose) printf("USAGE EXAMPLE\n"
                            "=============\n");
// ```
// Then, we want to actually use our hash table on `Future` objects.  We create
// an array of `Future`s based on data that was originally from some external
// source:
// ```
        Future futures[] = { Future("Swiss Franc", 'F', 2014),
                             Future("US Dollar", 'G', 2015),
                             Future("Canadian Dollar", 'Z', 2014),
                             Future("British Pound", 'M', 2015),
                             Future("Deutsche Mark", 'X', 2016),
                             Future("Eurodollar", 'Q', 2017)};
        enum { NUM_FUTURES = sizeof futures / sizeof *futures };
// ```
// Next, we create our HashTable `hashTable`.  We pass the functor that we
// defined above as the second argument:
// ```
        HashTable<Future, HashFuture> hashTable(futures, NUM_FUTURES);
// ```
// Now, we verify that each element in our array registers with count:
// ```
        for ( int i = 0; i < 6; ++i) {
            ASSERT(hashTable.contains(futures[i]));
        }
// ```
// Finally, we verify that futures not in our original array are correctly
// identified as not being in the set:
// ```
        ASSERT(!hashTable.contains(Future("French Franc", 'N', 2019)));
        ASSERT(!hashTable.contains(Future("Swiss Franc", 'X', 2014)));
        ASSERT(!hashTable.contains(Future("US Dollar", 'F', 2014)));

      } break;
      case 6: {
        // --------------------------------------------------------------------
        // TESTING HASHING AS SEPARATE SEGMENTS
        //
        // Concerns:
        // 1. Hashing memory as a single segment yields the same result as
        //    hashing it in multiple segments.
        //
        // Plan:
        // 1. Use a random number generator to fill a buffer and a seed buffer
        //    with random data.
        //
        // 2. Hash it as a single segment and store the result.
        //
        // 3. Iterate 10 times, each time randomly hashing it as multiple
        //    segments, and compare the result to the single segment result.
        //
        // Tesing:
        //   HASHING AS SEPARATE SEGMENTS
        // --------------------------------------------------------------------

        enum { k_MAX_LEN = 1024 };

        for (unsigned ti = 0; ti < k_MAX_LEN; ++ti) {
            const size_t LEN = ti;

            u::RandGen rand;
            rand.num(ti);

            char buffer[k_MAX_LEN];
            rand.randMemory(buffer, LEN);

            char seedBuffer[Obj::k_SEED_LENGTH];
            rand.randMemory(seedBuffer, Obj::k_SEED_LENGTH);

            Obj oneShotHash(seedBuffer);
            oneShotHash(buffer, LEN);
            const Obj::result_type oneShotResult = oneShotHash.computeHash();

            for (int tj = 0; tj < 10; ++tj) {
                Obj multiShotHash(seedBuffer);

                for (size_t offset = 0, subLen; offset < LEN;
                                                            offset += subLen) {
                    subLen = rand.num() % (LEN + 1 - offset);
                    ASSERT(offset + subLen <= LEN);

                    multiShotHash(buffer + offset, subLen);
                }

                Obj::result_type multiShotResult = multiShotHash.computeHash();

                ASSERT(oneShotResult == multiShotResult);
            }
        }
      } break;
      case 5: {
        // --------------------------------------------------------------------
        // TESTING `k_SEED_LENGTH`
        //   The class is a seeded algorithm and should expose a
        //   `k_SEED_LENGTH` enum that matches the underlying hashing
        //   algorithm.
        //
        // Concerns:
        // 1. `k_SEED_LENGTH` is publicly accessible.
        //
        // 2. `k_SEED_LENGTH` has the correct value.
        //
        // Plan:
        // 1. Access `k_SEED_LENGTH` and ASSERT it is equal to the value
        //    defined by 'bslh::WyHashInternalAlgorithm. (C-1,2)
        //
        // Testing:
        //   enum { k_SEED_LENGTH = InternalHashAlgorithm::k_SEED_LENGTH };
        // --------------------------------------------------------------------

        if (verbose) printf("\nTESTING `k_SEED_LENGTH`"
                            "\n=======================\n");

        if (verbose) printf("Access `k_SEED_LENGTH` and ASSERT it is equal to"
                            " the value defined by"
                            " `bslh::WyHashIncrementalAlgorithm`. (C-1,2)\n");
        {
            ASSERT(int(WyHashIncrementalAlgorithm::k_SEED_LENGTH) ==
                   int(Obj::k_SEED_LENGTH));
        }

      } break;
      case 4: {
        // --------------------------------------------------------------------
        // TESTING `result_type` TYPEDEF
        //   Verify that the class offers the result_type typedef that needs to
        //   be exposed by all `bslh` hashing algorithms
        //
        // Concerns:
        // 1. The typedef `result_type` is publicly accessible and an alias for
        //    `bslh::WyHashIncrementalAlgorithm::result_type`.
        //
        // 2. `computeHash()` returns `result_type`
        //
        // Plan:
        // 1. ASSERT the typedef is accessible and is the correct type using
        //    `bslmf::IsSame`. (C-1)
        //
        // 2. Declare the expected signature of `computeHash()` and then assign
        //    to it.  If it compiles, the test passes. (C-2)
        //
        // Testing:
        //   typedef InternalHashAlgorithm::result_type result_type;
        // --------------------------------------------------------------------

        if (verbose) printf("\nTESTING `result_type` TYPEDEF"
                            "\n=============================\n");

        if (verbose) printf("ASSERT the typedef is accessible and is the"
                            " correct type using `bslmf::IsSame`. (C-1)\n");
        {
            ASSERT((bslmf::IsSame<
                             Obj::result_type,
                             WyHashIncrementalAlgorithm::result_type>::value));
        }

        if (verbose) printf("Declare the expected signature of `computeHash()`"
                            " and then assign to it.  If it compiles, the test"
                            " passes. (C-2)\n");
        {
            Obj::result_type (Obj::*expectedSignature) ();

            expectedSignature = &Obj::computeHash;
            (void)expectedSignature;
        }

      } break;
      case 3: {
        // --------------------------------------------------------------------
        // TESTING TESTING `operator()` AND `computeHash()`
        //   Verify the class provides an overload for the function call
        //   operator that can be called with some bytes and a length.  Verify
        //   that calling `operator()` will permute the algorithm's internal
        //   state as specified by the underlying hashing algorithm
        //   (bslh::WyHashIncrementalAlgorithm).  Verify that `computeHash()`
        //   returns the final value specified by the canonical implementation
        //   of the underlying hashing algorithm.
        //
        // Concerns:
        // 1. The function call operator is callable.
        //
        // 2. The `computeHash()` function is callable.
        //
        // 3. The output of calling `operator()` and then `computeHash()`
        //    matches the output of the underlying hashing algorithm.
        //
        // 4. `operator()` does a BSLS_ASSERT for null pointers and non-zero
        //    length, and not for null pointers and zero length.
        //
        // Plan:
        // 1. Hash a number of values with `bslh::DefaultSeededHashAlgorithm`
        //    and `bslh::WyHashIncrementalAlgorithm` and verify that the
        //    outputs match.  (C-1,2,3)
        //
        // 2. Call `operator()` with a null pointer. (C-4)
        //
        // Testing:
        //   void operator()(void const* key, size_t len);
        //   result_type computeHash();
        // --------------------------------------------------------------------

        if (verbose) printf(
                       "\nTESTING TESTING `operator()` AND `computeHash()`"
                       "\n================================================\n");

        static const struct {
            int                  d_line;
            const char           d_value [21];
        } DATA[] = {
           // LINE           DATA
            {  L_,                     "1",},
            {  L_,                    "12",},
            {  L_,                   "123",},
            {  L_,                  "1234",},
            {  L_,                 "12345",},
            {  L_,                "123456",},
            {  L_,               "1234567",},
            {  L_,              "12345678",},
            {  L_,             "123456789",},
            {  L_,            "1234567890",},
            {  L_,           "12345678901",},
            {  L_,          "123456789012",},
            {  L_,         "1234567890123",},
            {  L_,        "12345678901234",},
            {  L_,       "123456789012345",},
            {  L_,      "1234567890123456",},
            {  L_,     "12345678901234567",},
            {  L_,    "123456789012345678",},
            {  L_,   "1234567890123456789",},
            {  L_,  "12345678901234567890",},
        };
        const int NUM_DATA = sizeof DATA / sizeof *DATA;

        if (verbose) printf("Hash a number of values with"
                            " `bslh::DefaultSeededHashAlgorithm` and"
                            " `bslh::WyHashIncrementalAlgorithm` and verify"
                            " that the outputs match. (C-1,2,3)\n");
        {
            for (int i = 0; i != NUM_DATA; ++i) {
                const char *VALUE = DATA[i].d_value;

                if (veryVerbose) printf("Hashing: %s\n with"
                                        " `bslh::DefaultSeededHashAlgorithm`"
                                        " and"
                                        " `bslh::WyHashIncrementalAlgorithm`",
                                        VALUE);

                Obj                        contiguousHash(globalSeed);
                WyHashIncrementalAlgorithm cannonicalHashAlgorithm(globalSeed);

                cannonicalHashAlgorithm(VALUE, strlen(VALUE));
                contiguousHash(VALUE, strlen(VALUE));

                const Obj::result_type contHash = contiguousHash.computeHash();
                const WyHashIncrementalAlgorithm::result_type canHash =
                                         cannonicalHashAlgorithm.computeHash();

                BSLMF_ASSERT((bsl::is_same<
                             Obj::result_type,
                             WyHashIncrementalAlgorithm::result_type>::value));

                ASSERTV(contHash, canHash, contHash == canHash);
            }
        }

        if (verbose) printf("Call `operator()` with null pointers. (C-4)\n");
        {
            const char data[5] = {'a', 'b', 'c', 'd', 'e'};

            bsls::AssertTestHandlerGuard guard;

            ASSERT_FAIL(Obj(globalSeed).operator()(   0, 5));
            ASSERT_PASS(Obj(globalSeed).operator()(   0, 0));
            ASSERT_PASS(Obj(globalSeed).operator()(data, 5));
        }

      } break;
      case 2: {
        // --------------------------------------------------------------------
        // TESTING CREATORS
        //   Ensure that the implicit destructor and the explicit parameterized
        //   constructor are publicly callable.  Note that a null pointer is
        //   not tested here, because there is no way to perform a BSLS_ASSERT
        //   before dereferenceing the pointer (without a performance penalty).
        //
        // Concerns:
        // 1. Objects can be created using the parameterized constructor.
        //
        // 2. Objects can be destroyed.
        //
        // Plan:
        // 1. Create a `DefaultSeededHashAlgorithm` with the parameterized
        //    constructor and allow it to leave scope to be destroyed. (C-1,2)
        //
        // Testing:
        //   DefaultSeededHashAlgorithm(const void *seed);
        //   ~DefaultSeededHashAlgorithm();
        // --------------------------------------------------------------------

        if (verbose)
            printf("\nTESTING CREATORS"
                   "\n================\n");

        if (verbose) printf("Create a `DefaultSeededHashAlgorithm` with the"
                            " parameterized constructor and allow it to leave"
                            " scope to be destroyed. (C-1,2)\n");
        {
            Obj alg1(globalSeed);
        }

      } break;
      case 1: {
        // --------------------------------------------------------------------
        // BREATHING TEST
        //   This case exercises (but does not fully test) basic functionality.
        //
        // Concerns:
        // 1. The class is sufficiently functional to enable comprehensive
        //    testing in subsequent test cases.
        //
        // Plan:
        // 1. Create an instance of `bsl::DefaultSeededHashAlgorithm`. (C-1)
        //
        // 2. Verify different hashes are produced for different c-strings.
        //    (C-1)
        //
        // 3. Verify the same hashes are produced for the same c-strings. (C-1)
        //
        // 4. Verify different hashes are produced for different `int`s. (C-1)
        //
        // 5. Verify the same hashes are produced for the same `int`s. (C-1)
        //
        // Testing:
        //   BREATHING TEST
        // --------------------------------------------------------------------

        if (verbose) printf("\nBREATHING TEST"
                            "\n==============\n");

        if (verbose) printf("Instantiate `bsl::DefaultSeededHashAlgorithm`\n");
        {
            Obj hashAlg(globalSeed);
        }

        if (verbose) printf("Verify different hashes are produced for"
                            " different c-strings.\n");
        {
            Obj hashAlg1(globalSeed);
            Obj hashAlg2(globalSeed);
            const char * str1 = "Hello World";
            const char * str2 = "Goodbye World";
            hashAlg1(str1, strlen(str1));
            hashAlg2(str2, strlen(str2));
            ASSERT(hashAlg1.computeHash() != hashAlg2.computeHash());
        }

        if (verbose) printf("Verify the same hashes are produced for the same"
                            " c-strings.\n");
        {
            Obj hashAlg1(globalSeed);
            Obj hashAlg2(globalSeed);
            const char * str1 = "Hello World";
            const char * str2 = "Hello World";
            hashAlg1(str1, strlen(str1));
            hashAlg2(str2, strlen(str2));
            ASSERT(hashAlg1.computeHash() == hashAlg2.computeHash());
        }

        if (verbose) printf("Verify different hashes are produced for"
                            " different `int`s.\n");
        {
            Obj hashAlg1(globalSeed);
            Obj hashAlg2(globalSeed);
            int int1 = 123456;
            int int2 = 654321;
            hashAlg1(&int1, sizeof(int));
            hashAlg2(&int2, sizeof(int));
            ASSERT(hashAlg1.computeHash() != hashAlg2.computeHash());
        }

        if (verbose) printf("Verify the same hashes are produced for the same"
                            " `int`s.\n");
        {
            Obj hashAlg1(globalSeed);
            Obj hashAlg2(globalSeed);
            int int1 = 123456;
            int int2 = 123456;
            hashAlg1(&int1, sizeof(int));
            hashAlg2(&int2, sizeof(int));
            ASSERT(hashAlg1.computeHash() == hashAlg2.computeHash());
        }
      } break;

      default: {
        fprintf(stderr, "WARNING: CASE `%d' NOT FOUND.\n", test);
        testStatus = -1;
      }
    }

    if (testStatus > 0) {
        fprintf(stderr, "Error, non-zero test status = %d.\n", testStatus);
    }

    return testStatus;
}

// ----------------------------------------------------------------------------
// Copyright 2014 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
